//! 错误处理的最高层，调用其他库返回的操作，在foreign_links中包装
//! 业务逻辑的操作，错误类型是String类型，问号分隔的。例如: "1034:访问失败".
//! 通过foreign_links我们方便的包装从其他库中的错误。
//! 通过Msg(String):我们方便的自定义业务的状态码和消息.
//!
use axum::{
    http::{header::ToStrError, Error as HttpError, StatusCode},
    response::{IntoResponse, Response},
    Error as AxumError, Json,
};

use error_chain::error_chain;
use sea_orm::error::{ColumnFromStrErr, DbErr};
use serde_json::Error as JsonError;
use std::{
    io::Error as IoError,
    str::{FromStr, Utf8Error},
};

error_chain! {
    foreign_links {
        DbColumnFromStrErr(ColumnFromStrErr);
        DbErr(DbErr);
        HttpToStrError(ToStrError);
        HttpError(HttpError);
        Utf8Error(Utf8Error);
        AxumError(AxumError);
        JsonError(JsonError);
        IoError(IoError);
    }

    errors {
        StatusCodeMsg(code: StatusCode, msg: String) {
             description("status code and message")
             display("status code: {}, msg: {}", code, msg)
        }
    }
}

impl IntoResponse for Error {
    #[tracing::instrument]
    fn into_response(self) -> Response {
        let (status, error_message) = error_to_tuple(&self);

        let body = Json(serde_json::json!({
            "code": status.as_str(),
            "message": error_message
        }));
        (status, body).into_response()
    }
}

pub fn error_to_tuple(err: &Error) -> (StatusCode, String) {
    tracing::error!("{}", err);
    match err.kind() {
        ErrorKind::DbColumnFromStrErr(err) => {
            tracing::error!("数据库格式转换错误: {}", err);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                "数据库格式转换错误".to_string(),
            )
        }

        ErrorKind::DbErr(err) => {
            tracing::error!("数据库操作错误: {}", err);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                "数据库内部操作错误".to_string(),
            )
        }

        ErrorKind::StatusCodeMsg(code, msg) => (code.clone(), msg.clone()),

        ErrorKind::Msg(msg) => {
            // msg由问号分隔，前面是StatusCode， 后面是错误信息
            let code_and_msg = msg.split(":").collect::<Vec<_>>();
            (
                StatusCode::from_str(code_and_msg[0]).unwrap(),
                code_and_msg[1].to_owned(),
            )
        }

        ErrorKind::HttpToStrError(err) => {
            tracing::error!("{}", err);
            (
                StatusCode::BAD_REQUEST,
                "http请求数据不是有效的utf8编码".to_string(),
            )
        }

        ErrorKind::HttpError(err) => {
            tracing::error!("{}", err);
            (StatusCode::INTERNAL_SERVER_ERROR, "内部错误".to_string())
        }

        ErrorKind::Utf8Error(err) => {
            tracing::error!("{}", err);
            (StatusCode::BAD_REQUEST, "转换Utf8失败".to_owned())
        }

        ErrorKind::AxumError(err) => {
            tracing::error!("{}", err);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                "内部框架错误".to_string(),
            )
        }
        ErrorKind::JsonError(err) => {
            tracing::error!("{}", err);
            (StatusCode::BAD_REQUEST, "jsons失败".to_string())
        }
        ErrorKind::IoError(err) => {
            tracing::error!("{}", err);
            (StatusCode::INTERNAL_SERVER_ERROR, "内部IO错误".to_string())
        }

        ErrorKind::__Nonexhaustive {} => panic!(),
    }
}

pub fn error_msg_new(code: StatusCode, msg: &str) -> String {
    format!("{}:{}", code, msg)
}

impl From<(StatusCode, &str)> for Error {
    fn from(s: (StatusCode, &str)) -> Self {
        Error::from_kind(ErrorKind::StatusCodeMsg(s.0, s.1.to_string()))
    }
}
